做另一面的自己

Android Hook 技术

hook一事看似神秘,其实并不是那么难,希望各位看官看过本文之后能有所收获。

本次是hook Android的点击事件,也就是OnClickListenerhook的意义在于你能在调用setOnClickListener后做些其他的事,其他一些你想和所有点击事件一起处理的事,那么在这里,我就以埋点为例吧。

1
2
3
4
@Override
public void onClick(View view) {
view.setTag(R.id.hook, "I'm hooking now.....");
}

这是一个普通的点击事件,可是弹出来的结果是Toast了个 I'm hooking now......

分析下源码:

首先来看看android.view.View中的这块代码,mOnClickListener变量静静的在这里(这里还有别的事件哦,比如OnLongClickListener等,大家学完之后可以试着hook下别的),我们需要做的就是移花接木,把自己的花替换掉这个木,mOnClickListener是ListenerInfo这个类的成员变量,那继续看看ListenerInfo在View的哪里被初始化了,因为我们最开始拿到的只有View这一个对象。

没错,找到了,getListenerInfo()干了这件事,我们从这个方法入手先把ListenerInfo拿下,然后再移花接木。

实现

hook的过程就是充分利用java反射机制的过程,几行代码搞定,我们来看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void hook(View view) {
try {
Class clazzView = Class.forName("android.view.View");
Method method = clazzView.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);
Object listenerInfo = method.invoke(view);
Class clazzInfo = Class.forName("android.view.View$ListenerInfo");
Field field = clazzInfo.getDeclaredField("mOnClickListener");
field.set(listenerInfo, new HookListener((View.OnClickListener) field.get(listenerInfo)));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}

由于移花接木有个本质不能忘,那就是尊重原有类型,因此,我们的木也得实现View.OnClickListener接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static class HookListener implements View.OnClickListener {
private View.OnClickListener mOriListener;
public HookListener(View.OnClickListener onClickListener) {
mOriListener = onClickListener;
}
@Override
public void onClick(View view) {
if (mOriListener != null) {
mOriListener.onClick(view);
}
String text = (String)view.getTag(R.id.hook);
Toast.makeText(view.getContext(), text, Toast.LENGTH_LONG).show();
}
}

以上代码就是我们的木,为了看起来更简单,我直接通过构造函数把原来OnClickListener给传过来了,然后在新的HookListeneronClick()里把原来的事件继续完成,并加上自己想猥琐欲为的一些事情。

这只是简单的hook了一个onClick事件,还有很多更复杂的操作需要阅读源码,然后作相应的hook操作。希望对大家有帮助,我也是初学者。